package com.kystar.muxplayer;

import android.annotation.SuppressLint;
import android.app.Application;
import android.content.res.Resources;
import android.os.Handler;
import android.util.Size;
import com.kystar.muxplayer.base.TLog;
import com.kystar.muxplayer.manager.PvmManager;
import com.kystar.muxplayer.manager.WindowsManager;
import com.kystar.muxplayer.model.Bound;
import com.kystar.muxplayer.model.Layer;
import com.kystar.muxplayer.model.LayerType;
import com.kystar.muxplayer.model.Output;
import com.kystar.muxplayer.model.PGMConfig;
import com.kystar.muxplayer.model.PVWConfig;
import com.kystar.muxplayer.model.Preset;
import com.kystar.muxplayer.model.Settings;
import com.kystar.muxplayer.model.Source;
import com.kystar.muxplayer.model.SourceType;
import com.kystar.muxplayer.protocol.CodeCmd;
import com.kystar.muxplayer.protocol.Connection;
import com.kystar.muxplayer.protocol.code.get.CG82_ConcatMode;
import com.kystar.muxplayer.protocol.code.get.CG83_OutPutInfo;
import com.kystar.muxplayer.protocol.code.get.CG84_ScreenSize;
import com.kystar.muxplayer.protocol.code.get.CG87_Language;
import com.kystar.muxplayer.protocol.code.get.CG88_CutTakeEffect;
import com.kystar.muxplayer.protocol.code.get.CG89_TakeMode;
import com.kystar.muxplayer.protocol.code.get.CG8B_LayerState;
import com.kystar.muxplayer.protocol.code.get.CG8C_LayerParam;
import com.kystar.muxplayer.protocol.code.get.CG8D_SourceStatus;
import com.kystar.muxplayer.protocol.code.get.CG8E_SourceInfo;
import com.kystar.muxplayer.protocol.code.get.CG8F_PgmConfig;
import com.kystar.muxplayer.protocol.code.get.CG91_Preset;
import com.kystar.muxplayer.protocol.code.get.CG93_Version;
import com.kystar.muxplayer.protocol.connection.DeviceConnection;
import com.kystar.muxplayer.protocol.connection.LogConnection;
import com.kystar.muxplayer.protocol.connection.MockConnection;
import io.reactivex.Observable;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.TimeoutException;
@SuppressLint("CheckResult")
public class BaseApp extends Application {

    Connection mConnection;
    static BaseApp _instance;
    private Handler mHandler;
    public static Resources mResources;
    private Settings mSettings;

    public static BaseApp getApp() {
        return _instance;
    }

    public static Settings getSettings() {
        return _instance.mSettings;
    }

    public static void setSettings(Settings settings) {

        if (_instance.mSettings == null) {
            _instance.mSettings = settings;
        } else {
            //判断更改的数据，通知ui刷新
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
        _instance = this;
        mHandler = new Handler();
        connection();
        mResources = getResources();
    }

    public boolean connection() {
        try {
            if (BuildConfig.mockConnection) {
                mConnection = new MockConnection();
            } else {
                mConnection = new DeviceConnection();
            }
            if (BuildConfig.connectLog) {
                mConnection = new LogConnection(mConnection);
            }

        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    private static <T extends CodeCmd> T _send(T cmd) throws IOException, TimeoutException {
        if (_instance.mConnection == null) {
            return null;
        }
        _instance.mConnection.write(cmd);
        return cmd;
    }

    public static <T extends CodeCmd> Observable<T> send(T cmd) {
        return Observable.create((ObservableOnSubscribe<T>) emitter -> {
            try {
                _send(cmd);
                emitter.onNext(cmd);//处理超时问题
            } catch (TimeoutException e) {
                emitter.onError(e);
            } catch (IOException e) {
                emitter.onError(e);
                return;
            }
            emitter.onComplete();
        })
//            .subscribeOn(Schedulers.io())
            .subscribeOn(Schedulers.single())
            .observeOn(AndroidSchedulers.mainThread());
    }


    public static <T extends CodeCmd> Observable<T> send(final T... cmds) {

        return Observable.create((ObservableOnSubscribe<T>) emitter -> {
            for (T cmd : cmds) {
                try {
                    _send(cmd);
                    emitter.onNext(cmd);//处理超时问题
                } catch (Exception e) {
                    emitter.onError(e);
                    return;
                }
            }
            emitter.onComplete();
        }).subscribeOn(Schedulers.single())
            .observeOn(AndroidSchedulers.mainThread());
    }



    public static void updateLayers() {
        Observable.create((ObservableOnSubscribe<Layer[]>) emitter -> {
            try {
                Layer[] layers = getSettings().getPVWConfig().getLayers();
                for (int i = 0; i < 4; i++) {
                    Layer layer = layers[i];
                    LayerType type = layer.getLayerType();
                    _send(CG8B_LayerState.createSingle(type)).loadLayer(layer);
                    if (layer.isVisible()) {

                        Bound bound = _send(CG8C_LayerParam.createRect(type)).getBound();
                        layer.setViewBound(bound);

                        //TODO 增加循环
                        bound = _send(CG8C_LayerParam.createClip(type)).getBound();
                        layer.setSourceBound(bound);
                        layer.setFilterColor(_send(CG8C_LayerParam.createCutout(type)).getInt());
                        layer.setFeatherRadius(_send(CG8C_LayerParam.createFeather(type)).getInt());
                        layer.setAlpha(_send(CG8C_LayerParam.createAlpha(type)).getInt());
                    }
                    TLog.e(layer);
                }
                emitter.onNext(layers);
            } catch (Exception e) {
                emitter.onError(e);
                return;
            }

            emitter.onComplete();
        }).subscribeOn(Schedulers.single())
            .observeOn(AndroidSchedulers.mainThread()).subscribe(layers->{
            PvmManager.getPvmSingleInstance().update(layers);
            WindowsManager.getInstance().setLayers(layers);
            });
    }

    public static Observable<Layer> getLayer(LayerType type) {
        return Observable.create((ObservableOnSubscribe<Layer>)emitter->{
            Layer layer = new Layer();
            layer.setLayerType(type);
            _send(CG8B_LayerState.createSingle(type)).loadLayer(layer);
            Bound bound = _send(CG8C_LayerParam.createRect(type)).getBound();
            layer.setViewBound(bound);

            bound = _send(CG8C_LayerParam.createClip(type)).getBound();
            layer.setSourceBound(bound);
            layer.setFilterColor(_send(CG8C_LayerParam.createCutout(type)).getInt());
            layer.setFeatherRadius(_send(CG8C_LayerParam.createFeather(type)).getInt());
            layer.setAlpha(_send(CG8C_LayerParam.createAlpha(type)).getInt());
            emitter.onNext(layer);
            emitter.onComplete();
        }).subscribeOn(Schedulers.single()).observeOn(AndroidSchedulers.mainThread());

    }

    public static Observable<Settings> loadAll() {
        return Observable.create((ObservableOnSubscribe<Settings>) emitter -> {
            Settings settings = new Settings();

            PVWConfig pvwConfig = new PVWConfig();
            settings.setPVWConfig(pvwConfig);
//            pvwConfig.setCurrentPreset(); //TODO
            {
                CG88_CutTakeEffect cmd = _send(CG88_CutTakeEffect.create());
                pvwConfig.setEffectType(cmd.getEffectType());
                pvwConfig.setEffectDuration(cmd.getDuration());
            }
            {
                Layer[] layers = new Layer[4];
                for (int i = 0; i < 4; i++) {
                    Layer layer = new Layer();
                    layers[i] = layer;
                    layer.setLayerType(LayerType.values()[i]);
                    LayerType type = layer.getLayerType();
                    _send(CG8B_LayerState.createSingle(type)).loadLayer(layer);
                    Bound bound = _send(CG8C_LayerParam.createRect(type)).getBound();
                    layer.setViewBound(bound);
                    bound = _send(CG8C_LayerParam.createClip(type)).getBound();
                    layer.setSourceBound(bound);
                    layer.setFilterColor(_send(CG8C_LayerParam.createCutout(type)).getInt());
                    layer.setFeatherRadius(_send(CG8C_LayerParam.createFeather(type)).getInt());
                    layer.setAlpha(_send(CG8C_LayerParam.createAlpha(type)).getInt());
                    TLog.e(layer);
                }
                pvwConfig.setLayers(layers);
            }
            pvwConfig.setTakeCopy(_send(CG89_TakeMode.create()).isTakeCopy());

            PGMConfig PGMConfig = new PGMConfig();
            settings.setPGMConfig(PGMConfig);
            {
                CG8F_PgmConfig cmd = _send(CG8F_PgmConfig.create());
                PGMConfig.setBlack(cmd.isBlack());
                PGMConfig.setFreeze(cmd.isFreeze());
            }

            Preset[] presets = _send(CG91_Preset.createPresetList()).getPresets();
            settings.setPresets(presets);

            Source[] sources = new Source[8];
            SourceType[] sourceTypes = {SourceType.DVI, SourceType.VGA, SourceType.DP1,
                SourceType.DP2, SourceType.HDMI, SourceType.SDI, SourceType.USB, SourceType.None};
            boolean[] sourceStatus = _send(CG8D_SourceStatus.create()).getStatus();
            for (int i = 0; i < 8; i++) {
                Source source = new Source();
                sources[i] = source;

                SourceType sourceType = sourceTypes[i];
                source.setSourceId(sourceType.ordinal());
                source.setType(sourceTypes[i]);
                source.setValid(sourceStatus[i]);

                if (source.getType() == SourceType.None) {
                    continue;
                }
                source.setBrightness(
                    _send(CG8E_SourceInfo.createSourceBrightInfo(source.getSourceId()))
                        .getBright());
                source.setEdid(
                    _send(CG8E_SourceInfo.createSourceEdidInfo(source.getSourceId())).getEDID());
                source.setBackup(_send(CG8E_SourceInfo.createSourceBackupInfo(source.getSourceId()))
                    .getBackup());
            }
            settings.setSources(sources);

            Output output = new Output();
            {
                output.setConcatIndex(_send(CG82_ConcatMode.create()).getConcatMode());
                CG83_OutPutInfo cmd = _send(CG83_OutPutInfo.create());
                output.setScreenSize(cmd.getScreenSize());
                output.setFrequency(cmd.getFreqType());
                output.setSizes(_send(CG84_ScreenSize.create()).getSize());
//                TLog.e(output.getScreenSize(), output.size());
//                TLog.e(Arrays.asList(output.getSizes()), output.getConcatIndex());
            }
            settings.setOutput(output);
            settings.setVersion(_send(CG93_Version.create()).getVersion());

            settings.getVersion().setLanguage(_send(CG87_Language.create()).getLanguage());

            setSettings(settings);
            emitter.onNext(settings);
            emitter.onComplete();
        }).subscribeOn(Schedulers.single()).observeOn(AndroidSchedulers.mainThread());
    }

    public static Bound toLocal(Bound bound, int frameWidth, int frameHeight) {
        Size out = getSettings().getOutput().size();
        float sx =  frameWidth / (float)out.getWidth();
        float sy = frameHeight / (float)out.getHeight();
        return new Bound(Math.round(bound.x * sx), Math.round(bound.y * sy),
            Math.round(bound.w * sx), Math.round(bound.h * sy));
    }

    public static Bound toWorld(Bound bound,int w,int h) {
        Size out = getSettings().getOutput().size();
        float sx =  w / (float)out.getWidth();
        float sy = h / (float)out.getHeight();
        return new Bound(Math.round(bound.x / sx), Math.round(bound.y / sy),
            Math.round(bound.w / sx), Math.round(bound.h / sy));
    }


    public static void updatePvw() {
        Observable.create((ObservableOnSubscribe<PVWConfig>) emmite->{
            PVWConfig pvwConfig = new PVWConfig();
            {
                CG88_CutTakeEffect cmd = _send(CG88_CutTakeEffect.create());
                pvwConfig.setEffectType(cmd.getEffectType());
                pvwConfig.setEffectDuration(cmd.getDuration());
            }
            {
                Layer[] layers = new Layer[4];
                for (int i = 0; i < 4; i++) {
                    Layer layer = new Layer();
                    layers[i] = layer;
                    layer.setLayerType(LayerType.values()[i]);
                    LayerType type = layer.getLayerType();
                    _send(CG8B_LayerState.createSingle(type)).loadLayer(layer);
                    Bound bound = _send(CG8C_LayerParam.createRect(type)).getBound();
                    layer.setViewBound(bound);
                    bound = _send(CG8C_LayerParam.createClip(type)).getBound();
                    layer.setSourceBound(bound);
                    layer.setFilterColor(_send(CG8C_LayerParam.createCutout(type)).getInt());
                    layer.setFeatherRadius(_send(CG8C_LayerParam.createFeather(type)).getInt());
                    layer.setAlpha(_send(CG8C_LayerParam.createAlpha(type)).getInt());
                    TLog.e(layer);
                }
                pvwConfig.setLayers(layers);
            }
            pvwConfig.setTakeCopy(_send(CG89_TakeMode.create()).isTakeCopy());
            getSettings().update(pvwConfig);
            emmite.onNext(pvwConfig);
            emmite.onComplete();
        }).subscribeOn(Schedulers.single()).observeOn(AndroidSchedulers.mainThread()).subscribe(pvwConfig->{

        },e->{

        });

    }

    public static void updateOutPut() {
        Observable.create((ObservableOnSubscribe<Output>) emitter -> {

            Output output = getSettings().getOutput();
            {
                output.setConcatIndex(_send(CG82_ConcatMode.create()).getConcatMode());
                CG83_OutPutInfo cmd = _send(CG83_OutPutInfo.create());
                output.setScreenSize(cmd.getScreenSize());
                output.setFrequency(cmd.getFreqType());
                output.setSizes(_send(CG84_ScreenSize.create()).getSize());
//                TLog.e(output.getScreenSize(), output.size());
//                TLog.e(Arrays.asList(output.getSizes()), output.getConcatIndex());
            }


            emitter.onNext(output);
            emitter.onComplete();
        }).subscribeOn(Schedulers.single()).observeOn(AndroidSchedulers.mainThread())
        .subscribe(cmd->{},e->{},BaseApp::updateLayers);
    }
}
